#include "GLFunction2DGraph.h"
#include "GLRenderingContext.h"
using namespace OpenGLWrapper;

GLFunction2DGraph::GLFunction2DGraph()
	: GLMath2DApplication()
{
	_graphsColors.clear();
	_numGraphs.clear();
	_graphsX.clear();
	_graphsY.clear();

	_linesColors.clear();
	_linesX.clear();
	_linesY.clear();

	_pointsColors.clear();
	_pointsX.clear();
	_pointsY.clear();

	strcpy_s(_currentDisplayedPoint, "");
	_textX = _textY = 0;
}

GLFunction2DGraph::~GLFunction2DGraph()
{
}

bool GLFunction2DGraph::Init()
{
	if (!GLMath2DApplication::Init())
		return false;

	return true;
}
void GLFunction2DGraph::Prepare(float dt)
{
	GLMath2DApplication::Prepare(dt);
}

void GLFunction2DGraph::RenderFunctions()
{
	// draw selected points
	auto renderingContext = GLRenderingContext::GetCurrentContext();
	renderingContext->RenderText(_textX, _textY, 0.0, _currentDisplayedPoint, Color(1.0, 1.0, 1.0), _fontSize);

	// draw functions
	for (size_t j = 0; j < _numGraphs.size(); j++)
	{
		glPointSize(6.0);
		glBegin(GL_POINTS);
		int step = int(_numGraphs[j] * 0.1);
		if (step <= 0)
			step = 1;
		glColor3d(_graphsColors[j].r, _graphsColors[j].g, _graphsColors[j].b);
		for (int i = 0; i < _numGraphs[j]; i+=step)
			glVertex2d(_graphsX[j][i] * _stepX / _incX, _graphsY[j][i] * _stepY / _incY);
		glEnd();
		glPointSize(1.0);

		glBegin(GL_LINE_STRIP);
		glColor3d(_graphsColors[j].r, _graphsColors[j].g, _graphsColors[j].b);
		for (int i = 0; i < _numGraphs[j]; i++)
			glVertex2d(_graphsX[j][i] * _stepX / _incX, _graphsY[j][i] * _stepY / _incY);
		glEnd();
	}

	glLineWidth(1.0);
}

void GLFunction2DGraph::RenderLines()
{
	glDisable( GL_LINE_SMOOTH );
	glLineWidth(2.0);
	glBegin(GL_LINES);
	for (size_t j = 0; j < _linesX.size(); j+=2)
	{
		glColor3d(_linesColors[j / 2].r, _linesColors[j / 2].g, _linesColors[j / 2].b);
		glVertex2d(_linesX[j] * _stepX / _incX, _linesY[j] * _stepY / _incY);
		glVertex2d(_linesX[j + 1] * _stepX / _incX, _linesY[j + 1] * _stepY / _incY);
	}
	glEnd();
	glLineWidth(1.0);
	glEnable( GL_LINE_SMOOTH );
}

void GLFunction2DGraph::RenderPoints()
{
	glPointSize(10.0);
	glBegin(GL_POINTS);
	for (size_t j = 0; j < _pointsColors.size(); j++)
	{
		glColor3d(_pointsColors[j].r, _pointsColors[j].g, _pointsColors[j].b);
		glVertex2d(_pointsX[j] * _stepX / _incX, _pointsY[j] * _stepY / _incY);
	}
	glEnd();
	glPointSize(1.0);
}

void GLFunction2DGraph::OnMathRender()
{
	RenderFunctions();
	RenderLines();
	RenderPoints();
}

void GLFunction2DGraph::OnMouseDown(int button, int x, int y)
{
	GLMath2DApplication::OnMouseDown(button, x, y);
}

void GLFunction2DGraph::OnMouseMove(int x, int y)
{
	GLMath2DApplication::OnMouseMove(x, y);

	strcpy_s(_currentDisplayedPoint, "");
	if (GetKeyState(VK_LMENU) & 0x80)
	{
		POINT p = { x, y };
		ClientToScreen(GetActiveWindow(), &p);
		x = p.x; y = p.y;
		_textX = (x - _currentWidth / 2.0);
		_textY = (_currentHeight / 2.0 - y);
		_textX /= _scaleValue;
		_textY /= _scaleValue;
		_textX -= _translateX;
		_textY -= _translateY;		

		double objX = (_textX / _stepX) * _incX;
		double objY = (_textY / _stepY) * _incY;
		sprintf_s(_currentDisplayedPoint, "(%.2g, %.2g)", objX, objY);
	}
}

void GLFunction2DGraph::Render()
{
	GLMath2DApplication::Render();	
}

void GLFunction2DGraph::Shutdown()
{
	GLMath2DApplication::Shutdown();
}

void GLFunction2DGraph::OnResize(int width, int height)
{
	GLMath2DApplication::OnResize(width, height);
}

void GLFunction2DGraph::AddFunction(double(*f)(double x), double sx, double ex, double step, Color color, bool reverse)
{
	_graphsColors.push_back(color);

	_numGraphs.push_back(int((ex - sx) / step));
	std::vector<double> pointsX, pointsY;
	for (int i = 0; i < _numGraphs[_numGraphs.size() - 1]; i++)
	{
		pointsX.push_back(sx + i * step);
		pointsY.push_back(f(sx + i * step));
	}
	if (reverse)
	{
		_graphsX.push_back(pointsY);
		_graphsY.push_back(pointsX);
	}
	else
	{
		_graphsX.push_back(pointsX);
		_graphsY.push_back(pointsY);
	}
}

void GLFunction2DGraph::AddLine(double sx, double sy, double ex, double ey, Color color)
{
	_linesColors.push_back(color);
	_linesX.push_back(sx); _linesX.push_back(ex);
	_linesY.push_back(sy); _linesY.push_back(ey);
}

void GLFunction2DGraph::AddPoint(double sx, double sy, Color color)
{
	_pointsColors.push_back(color);
	_pointsX.push_back(sx); 
	_pointsY.push_back(sy);
}